home *** CD-ROM | disk | FTP | other *** search
Text File | 1996-03-21 | 19.7 KB | 775 lines | [TEXT/ALFA] |
- // ===========================================================================
- //
- // GSImage.c
- //
- // Copyright (C) 1996 Apple Computer, Inc. All rights reserved.
- //
- // ===========================================================================
-
-
- // ===========================================================================
- // Includes
- // ===========================================================================
-
- #include <stdlib.h>
-
- #include "RAVE.h"
-
- #include "GSImage.h"
- #include "GSPicture.h"
- #include "GSColorTable.h"
- #include "GSUtilities.h"
- #include "GSError.h"
-
-
- // ===========================================================================
- // Constants
- // ===========================================================================
-
- #define kMaxImageCount 20
-
-
- // ===========================================================================
- // Types
- // ===========================================================================
-
- typedef struct TGSImage {
- TQAImagePixelType mPixelType;
- TGSPicture* mPicture;
- TQAImage mImages[kMaxImageCount];
- void* mMipMaps;
- Boolean mIsValidTexture;
- Boolean mIsValidBitmap;
- Boolean mIsValidMipMap;
- } TGSImage;
-
-
- // ===========================================================================
- // Private Prototypes
- // ===========================================================================
-
- TGSError
- GSImage_MakeBlackTransparent(
- TGSImage* inImage);
-
- TGSError
- GSImage_MakeMipMap(
- TGSImage* inImage);
-
- long
- AlphaChannel(
- TQAImagePixelType inPixelType,
- Byte* inPixelP);
-
- long
- RedChannel(
- TQAImagePixelType inPixelType,
- Byte* inPixelP);
-
- long
- GreenChannel(
- TQAImagePixelType inPixelType,
- Byte* inPixelP);
-
- long
- BlueChannel(
- TQAImagePixelType inPixelType,
- Byte* inPixelP);
-
- void
- SetARGB(
- TQAImagePixelType inPixelType,
- Byte* inPixelP,
- long inA,
- long inR,
- long inG,
- long inB);
-
-
- // ===========================================================================
- // Public Routines
- // ===========================================================================
-
- // ===========================================================================
- // GSImage_New
- // ===========================================================================
- TGSError
- GSImage_New(
- TGSImage** inImage)
- {
- *inImage = (TGSImage*) malloc(sizeof(TGSImage));
-
- if (*inImage == nil) {
- return kGSError_NotEnoughMemory;
- }
-
- (**inImage).mPixelType = kQAPixel_RGB32;
- (**inImage).mPicture = nil;
- (**inImage).mMipMaps = nil;
- (**inImage).mIsValidTexture = false;
- (**inImage).mIsValidBitmap = false;
- (**inImage).mIsValidMipMap = false;
-
- return kGSError_None;
- }
-
-
- // ===========================================================================
- // GSImage_NewFromPicture
- // ===========================================================================
- TGSError
- GSImage_NewFromPicture(
- TGSImage** inImage,
- TGSPicture* inPicture,
- TQAImagePixelType inPixelType,
- Boolean inMakeBlackTransparent,
- Boolean inMakeMipMap)
- {
- TGSError gsError;
-
- GSAssert_(inPicture);
-
- gsError = GSImage_New(inImage);
-
- if (gsError != kGSError_None) {
- GSImage_Delete(*inImage);
- return gsError;
- }
-
- (**inImage).mPixelType = inPixelType;
-
- // we are now responsible for the disposal of inPicture. we need to keep
- // it around because it contains the storage for the full-size texture,
- // which we'll need until this image is deleted.
- (**inImage).mPicture = inPicture;
-
- // fill in the first image in the array, which is exactly the same as
- // the inPicture
- (**inImage).mImages[0].width = GSPicture_GetWidth(inPicture);
- (**inImage).mImages[0].height = GSPicture_GetHeight(inPicture);
- (**inImage).mImages[0].rowBytes = GSPicture_GetRowBytes(inPicture);
- (**inImage).mImages[0].pixmap = GSPicture_GetPixMap(inPicture);
-
- if (inMakeBlackTransparent) {
- // make every black pixel be transparent and every non-black one
- // be opaque
- gsError = GSImage_MakeBlackTransparent(*inImage);
-
- if (gsError != kGSError_None) {
- GSImage_Delete(*inImage);
- return gsError;
- }
- }
-
- // if we've gotten here, then we at least have a valid bitmap image,
- // because bitmap dimensions don't have to be powers of 2
- (**inImage).mIsValidBitmap = true;
-
- if (GSIsPowerOf2((**inImage).mImages[0].width, nil) &&
- GSIsPowerOf2((**inImage).mImages[0].height, nil)) {
- // the dimensions of a texture must be a power of 2, so we have
- // a valid texture
- (**inImage).mIsValidTexture = true;
-
- if (inMakeMipMap) {
- // the caller wants mipmaps made for this texture
- gsError = GSImage_MakeMipMap(*inImage);
-
- if (gsError != kGSError_None) {
- GSImage_Delete(*inImage);
- return gsError;
- }
- }
- }
-
- return kGSError_None;
- }
-
-
- // ===========================================================================
- // GSImage_Delete
- // ===========================================================================
- void
- GSImage_Delete(
- TGSImage* inImage)
- {
- if (inImage == nil) {
- return;
- }
-
- if (inImage->mPicture != nil) {
- GSPicture_Delete(inImage->mPicture);
- inImage->mPicture = nil;
- }
-
- if (inImage->mMipMaps != nil) {
- free(inImage->mMipMaps);
- inImage->mMipMaps = nil;
- }
-
- free(inImage);
- }
-
-
- // ===========================================================================
- // GSImage_GetImages
- // ===========================================================================
- TQAImage*
- GSImage_GetImages(
- TGSImage* inImage)
- {
- GSAssert_(inImage);
-
- return inImage->mImages;
- }
-
-
- // ===========================================================================
- // GSImage_GetWidth
- // ===========================================================================
- long
- GSImage_GetWidth(
- TGSImage* inImage)
- {
- GSAssert_(inImage);
-
- return inImage->mImages[0].width;
- }
-
-
- // ===========================================================================
- // GSImage_GetHeight
- // ===========================================================================
- long
- GSImage_GetHeight(
- TGSImage* inImage)
- {
- GSAssert_(inImage);
-
- return inImage->mImages[0].height;
- }
-
-
- // ===========================================================================
- // GSImage_GetRowBytes
- // ===========================================================================
- long
- GSImage_GetRowBytes(
- TGSImage* inImage)
- {
- GSAssert_(inImage);
-
- return inImage->mImages[0].rowBytes;
- }
-
-
- // ===========================================================================
- // GSImage_GetPixelType
- // ===========================================================================
- TQAImagePixelType
- GSImage_GetPixelType(
- TGSImage* inImage)
- {
- GSAssert_(inImage);
-
- return inImage->mPixelType;
- }
-
-
- // ===========================================================================
- // GSImage_GetColorTable
- // ===========================================================================
- TGSColorTable*
- GSImage_GetColorTable(
- TGSImage* inImage)
- {
- GSAssert_(inImage);
-
- return GSPicture_GetColorTable(inImage->mPicture);
- }
-
-
- // ===========================================================================
- // GSImage_IsValidTexture
- // ===========================================================================
- Boolean
- GSImage_IsValidTexture(
- TGSImage* inImage)
- {
- GSAssert_(inImage);
-
- return inImage->mIsValidTexture;
- }
-
-
- // ===========================================================================
- // GSImage_IsValidBitmap
- // ===========================================================================
- Boolean
- GSImage_IsValidBitmap(
- TGSImage* inImage)
- {
- GSAssert_(inImage);
-
- return inImage->mIsValidBitmap;
- }
-
-
- // ===========================================================================
- // GSImage_IsValidMipMap
- // ===========================================================================
- Boolean
- GSImage_IsValidMipMap(
- TGSImage* inImage)
- {
- GSAssert_(inImage);
-
- return inImage->mIsValidMipMap;
- }
-
-
- // ===========================================================================
- // Private Routines
- // ===========================================================================
-
- // ===========================================================================
- // GSImage_MakeBlackTransparent
- // ===========================================================================
- TGSError
- GSImage_MakeBlackTransparent(
- TGSImage* inImage)
- {
- #define kARGB16mask_alpha (1 << 15)
- #define kARGB16mask_RGB ~(1 << 15)
-
- if (inImage->mPixelType == kQAPixel_ARGB16) {
- short* pixMap = (short*) inImage->mImages[0].pixmap;
- short* scanline;
- long pixelsPerRow = inImage->mImages[0].rowBytes / 2;
- long x, y;
-
- for (y = 0; y < inImage->mImages[0].height; y++) {
- // find the beginning of the next scanline
- scanline = pixMap + (y * pixelsPerRow);
-
- for (x = 0; x < inImage->mImages[0].width; x++) {
- if ((scanline[x] & kARGB16mask_RGB) == 0) {
- // the color of this pixel is black, so make it transparent.
- // and'ing the pixel with the inverse of kARGB16mask_alpha
- // turns off the high bit.
- scanline[x] &= ~kARGB16mask_alpha;
- } else {
- // the color of this pixel is not black, so make it opaque.
- // or'ing the pixel with kARGB16mask_alpha turns on the high bit.
- scanline[x] |= kARGB16mask_alpha;
- }
- } // endfor
- } // endfor
- } else if (inImage->mPixelType == kQAPixel_ARGB32) {
- typedef struct Pixel32 {
- unsigned char a; /* D31:24 */
- unsigned char r; /* D23:16 */
- unsigned char g; /* D15:8 */
- unsigned char b; /* D7:0 */
- } Pixel32;
-
- Pixel32* scanline;
- Pixel32* pixMap = (Pixel32*) inImage->mImages[0].pixmap;
- long pixelsPerRow = inImage->mImages[0].rowBytes / 4;
- long x, y;
-
- for (y = 0; y < inImage->mImages[0].height; y++) {
- // find the beginning of the next scanline
- scanline = pixMap + (y * pixelsPerRow);
-
- for (x = 0; x < inImage->mImages[0].width; x++) {
- if ((scanline[x].r == 0) && (scanline[x].g == 0) && (scanline[x].b == 0)) {
- // the color of this pixel is black, so make it transparent (alpha = 0)
- scanline[x].a = 0;
- } else {
- // the color of this pixel is not black, so make it opaque (alpha = 255)
- scanline[x].a = ~0;
- }
- } // endfor
- } // endfor
- }
-
- #undef kARGB16mask_alpha
- #undef kARGB16mask_RGB
-
- return kGSError_None;
- }
-
-
- // ===========================================================================
- // GSImage_MakeMipMap
- // ===========================================================================
- TGSError
- GSImage_MakeMipMap(
- TGSImage* inImage)
- {
- Byte* mipMapP;
- long nImages, nPixels;
- long width, height;
- long i, x, y;
- long halfBoxSize;
- unsigned short widthNBits, heightNBits;
- short pixelSize = 0;
-
- // we don't have a valid mipmap until we get to the end of this method
- inImage->mIsValidMipMap = false;
-
- switch (inImage->mPixelType) {
- case kQAPixel_Alpha1:
- case kQAPixel_CL4:
- case kQAPixel_CL8:
- // we can only mipmap 16- or 32-bit images, but don't return
- // an error, since this is an expected condition
- return kGSError_None;
- break;
-
- case kQAPixel_ARGB32:
- case kQAPixel_RGB32:
- pixelSize = 4;
- break;
-
- case kQAPixel_ARGB16:
- case kQAPixel_RGB16:
- pixelSize = 2;
- break;
-
- default:
- GSAssert_(!"Found unrecognized TQAImagePixelType in GSImage_MakeMipMap");
- break;
- } // endswitch
-
- // calculate the root-2 of of the width and height of the picture. they
- // have to be a power of 2 for us to mipmap the image, so return if
- // they're not.
- if (GSIsPowerOf2((unsigned) GSImage_GetWidth(inImage), &widthNBits) == false) {
- return kGSError_None;
- }
-
- if (GSIsPowerOf2((unsigned) GSImage_GetHeight(inImage), &heightNBits) == false) {
- return kGSError_None;
- }
-
- // allocate working space for the mip map images (starting with the
- // second-largest map)
- nImages = ((widthNBits > heightNBits) ? widthNBits : heightNBits) + 1;
-
- width = 1 << (widthNBits - 1);
- height = 1 << (heightNBits - 1);
-
- // count the number of pixels contained in all the mipmaps
- for (i = nImages - 1, nPixels = 0; i > 0; i--) {
- nPixels += width * height;
-
- // halve the width and height, making sure it's always at least 1
- width >>= 1;
- height >>= 1;
-
- if (width == 0) {
- width = 1;
- }
-
- if (height == 0) {
- height = 1;
- }
- }
-
- // create an array of bytes of the proper size
- inImage->mMipMaps = malloc(nPixels * pixelSize);
- mipMapP = (Byte*) inImage->mMipMaps;
-
- if (inImage->mMipMaps == nil) {
- // we don't have enough memory to create the mipmaps
- return;
- }
-
- width = 1 << (widthNBits - 1);
- height = 1 << (heightNBits - 1);
-
- // fill in the TQAImage info. the first image is full size, and we
- // already filled in its info in the constructor.
- for (i = 1; i < nImages; i++) {
- inImage->mImages[i].width = width;
- inImage->mImages[i].height = height;
- inImage->mImages[i].pixmap = mipMapP;
- inImage->mImages[i].rowBytes = width * pixelSize;
-
- // go to the beginning of the next pixmap
- mipMapP += width * height * pixelSize;
-
- // halve the width and height, making sure it's always at least 1
- width >>= 1;
- height >>= 1;
-
- if (width == 0) {
- width = 1;
- }
-
- if (height == 0) {
- height = 1;
- }
- }
-
- // halfBoxSize is used to round off the box filter of four pixels.
- halfBoxSize = 4 / 2;
-
- // Create the mipmapped images.
- for (i = 1; i < nImages; i++) {
- TQAImage* prevImage;
- TQAImage* image;
- long sourceRowBytes, targetRowBytes;
- Byte* sourceLine0;
- Byte* sourceLine1;
- Byte* targetLine;
-
- prevImage = &inImage->mImages[i - 1];
- image = &inImage->mImages[i];
-
- sourceLine0 = (Byte*) prevImage->pixmap;
-
- if (prevImage->height == 1) {
- // Source image has height of 1 (widthNBits must be greater than heightNBits).
- // Make sourceLine1 duplicate sourceLine0, and set sourceRowBytes to 0 so
- // we just keep re-reading the single source line.
- sourceRowBytes = 0;
- sourceLine1 = sourceLine0;
- } else {
- // Normal case. sourceLine1 points one scanline above sourceLine0.
- sourceRowBytes = prevImage->rowBytes;
- sourceLine1 = sourceLine0 + sourceRowBytes;
- }
-
- targetRowBytes = image->rowBytes;
- targetLine = (Byte*) image->pixmap;
-
- for (y = image->height; y > 0; y--) {
- Byte* sourcePixel0;
- Byte* sourcePixel1;
- Byte* targetPixel;
-
- sourcePixel0 = sourceLine0;
- sourcePixel1 = sourceLine1;
- targetPixel = targetLine;
-
- if (prevImage->width == 1) {
- // Source image has width 1 (heightNBits must be greater than widthNBits).
- // Use a special inner loop that doesn't advance past the first pixel of
- // the source lines.
- for (x = image->width; x > 0; x--) {
- long a, r, g, b;
-
- // Read and average two pixels from source into target.
- a = AlphaChannel(inImage->mPixelType, sourcePixel0) +
- (halfBoxSize >> 1);
- r = RedChannel(inImage->mPixelType, sourcePixel0) +
- (halfBoxSize >> 1);
- g = GreenChannel(inImage->mPixelType, sourcePixel0) +
- (halfBoxSize >> 1);
- b = BlueChannel(inImage->mPixelType, sourcePixel0) +
- (halfBoxSize >> 1);
-
- a += AlphaChannel(inImage->mPixelType, sourcePixel1);
- r += RedChannel(inImage->mPixelType, sourcePixel1);
- g += GreenChannel(inImage->mPixelType, sourcePixel1);
- b += BlueChannel(inImage->mPixelType, sourcePixel1);
-
- SetARGB(inImage->mPixelType, targetPixel, a >> 1, r >> 1, g >> 1, b >> 1);
-
- targetPixel += pixelSize;
- }
- } else {
- // Source image has at least width 2. This is the normal case.
- for (x = image->width; x > 0; x--) {
- long a, r, g, b;
-
- // Read and average four pixels from source into target.
- a = AlphaChannel(inImage->mPixelType, sourcePixel0) + halfBoxSize;
- r = RedChannel(inImage->mPixelType, sourcePixel0) + halfBoxSize;
- g = GreenChannel(inImage->mPixelType, sourcePixel0) + halfBoxSize;
- b = BlueChannel(inImage->mPixelType, sourcePixel0) + halfBoxSize;
-
- sourcePixel0 += pixelSize;
-
- a += AlphaChannel(inImage->mPixelType, sourcePixel1);
- r += RedChannel(inImage->mPixelType, sourcePixel1);
- g += GreenChannel(inImage->mPixelType, sourcePixel1);
- b += BlueChannel(inImage->mPixelType, sourcePixel1);
-
- sourcePixel1 += pixelSize;
-
- a += AlphaChannel(inImage->mPixelType, sourcePixel0);
- r += RedChannel(inImage->mPixelType, sourcePixel0);
- g += GreenChannel(inImage->mPixelType, sourcePixel0);
- b += BlueChannel(inImage->mPixelType, sourcePixel0);
-
- sourcePixel0 += pixelSize;
-
- a += AlphaChannel(inImage->mPixelType, sourcePixel1);
- r += RedChannel(inImage->mPixelType, sourcePixel1);
- g += GreenChannel(inImage->mPixelType, sourcePixel1);
- b += BlueChannel(inImage->mPixelType, sourcePixel1);
-
- sourcePixel1 += pixelSize;
-
- SetARGB(inImage->mPixelType, targetPixel,
-
- a >> 2, r >> 2, g >> 2, b >> 2);
-
- targetPixel += pixelSize;
- }
- }
-
- sourceLine0 += (sourceRowBytes << 1);
- sourceLine1 += (sourceRowBytes << 1);
- targetLine += targetRowBytes;
- }
- }
-
- // if we've gotten here, then we have a valid mipmap
- inImage->mIsValidMipMap = true;
-
- return kGSError_None;
- }
-
-
- // ===========================================================================
- // AlphaChannel
- // ===========================================================================
- long
- AlphaChannel(
- TQAImagePixelType inPixelType,
- Byte* inPixelP)
- {
- switch (inPixelType) {
- case kQAPixel_RGB32:
- return 255;
- break;
-
- case kQAPixel_RGB16:
- return 1;
- break;
-
- case kQAPixel_ARGB32:
- return inPixelP[0];
- break;
-
- case kQAPixel_ARGB16:
- return (inPixelP[0] & 0x80) >> 7;
- break;
- } // endswitch
-
- return 0;
- }
-
-
- // ===========================================================================
- // RedChannel
- // ===========================================================================
- long
- RedChannel(
- TQAImagePixelType inPixelType,
- Byte* inPixelP)
- {
- switch (inPixelType) {
- case kQAPixel_ARGB32:
- case kQAPixel_RGB32:
- return inPixelP[1];
- break;
-
- case kQAPixel_ARGB16:
- case kQAPixel_RGB16:
- return (inPixelP[0] & 0x7C) >> 2;
- break;
- } // endswitch
-
- return 0;
- }
-
-
- // ===========================================================================
- // GreenChannel
- // ===========================================================================
- long
- GreenChannel(
- TQAImagePixelType inPixelType,
- Byte* inPixelP)
- {
- switch (inPixelType) {
- case kQAPixel_ARGB32:
- case kQAPixel_RGB32:
- return inPixelP[2];
- break;
-
- case kQAPixel_ARGB16:
- case kQAPixel_RGB16:
- // the green bits straddle the two bytes
- return ((inPixelP[0] & 0x03) << 3) + ((inPixelP[1] & 0xE0) >> 5);
- break;
- } // endswitch
-
- return 0;
- }
-
-
- // ===========================================================================
- // BlueChannel
- // ===========================================================================
- long
- BlueChannel(
- TQAImagePixelType inPixelType,
- Byte* inPixelP)
- {
- switch (inPixelType) {
- case kQAPixel_ARGB32:
- case kQAPixel_RGB32:
- return inPixelP[3];
- break;
-
- case kQAPixel_ARGB16:
- case kQAPixel_RGB16:
- return (inPixelP[1] & 0x1F);
- break;
- } // endswitch
-
- return 0;
- }
-
-
- // ===========================================================================
- // SetARGB
- // ===========================================================================
- void
- SetARGB(
- TQAImagePixelType inPixelType,
- Byte* inPixelP,
- long inA,
- long inR,
- long inG,
- long inB)
- {
- switch (inPixelType) {
- case kQAPixel_ARGB32:
- case kQAPixel_RGB32:
- inPixelP[0] = (Byte) inA;
- inPixelP[1] = (Byte) inR;
- inPixelP[2] = (Byte) inG;
- inPixelP[3] = (Byte) inB;
- break;
-
- case kQAPixel_ARGB16:
- case kQAPixel_RGB16:
- inPixelP[0] = (Byte)
- ((inA & 1) << 7) +
- ((inR & 0x1F) << 2) +
- ((inG & 0x18) >> 3);
- inPixelP[1] = (Byte)
- ((inG & 0x07) << 5) +
- (inB & 0x1F);
- break;
- } // endswitch
- }
-